#pragma once
#include <iostream>
#include <vector>
#include <pthread.h>
#include <string>
#include <queue>
#include "Task.hpp"

struct threadData
{
  pthread_t tid;
  std::string threadName;
};

const int defaultCap = 5;

template <class T>
class ThreadPool
{
public:
  void Lock()
  {
    pthread_mutex_lock(&_mutex);
  }
  void Unlock()
  {
    pthread_mutex_unlock(&_mutex);
  }

  void Sleepthread()
  {
    pthread_cond_wait(&_cond, &_mutex);
  }

  void Wakeup()
  {
    pthread_cond_signal(&_cond);
  }

  bool IsEmptyQueue()
  {
    return _taskqueue.empty();
  }

  std::string GetThreadName(pthread_t tid)
  {
    for (auto &ti : _threadpool)
    {
      if (ti.tid == tid)
      {
        return ti.threadName;
      }
    }
    return "NONE";
  }

public:
  static void *HandlerTask(void *args)
  {
    ThreadPool<T> *tp = static_cast<ThreadPool<T> *>(args);
    std::string name = tp->GetThreadName(pthread_self());
    while (true)
    {
      tp->Lock();
      while (tp->IsEmptyQueue())
      {
        tp->Sleepthread();
      }
      T data = tp->pop();
      data();
      tp->Unlock();

      // printf("name: %s, complete a task: %d\n", name.c_str(), data);
      printf("name: %s, get a task: %s, result: %s\n", name.c_str(), data.getTask().c_str(), data.getResult().c_str());
    }
  }

  void start()
  {
    for (int i = 0; i < _threadpool.size(); i++)
    {
      std::string str = "thread-" + std::to_string(i + 1);
      _threadpool[i].threadName = str;
      pthread_create(&(_threadpool[i].tid), nullptr, HandlerTask, this);
    }
  }

  void push(const T &data)
  {
    Lock();
    _taskqueue.push(data);
    Wakeup();

    Unlock();
  }

  T pop()
  {
    T top = _taskqueue.front();
    _taskqueue.pop();

    return top;
  }

  // 懒汉模式的单利模式
  static ThreadPool<T> *GetIntance()
  {
    if (_tp == nullptr)
    {
      pthread_mutex_lock(&_lock);
      if (_tp == nullptr)
      {
        std::cout << "the first thread" << std::endl;
        _tp = new ThreadPool<T>();
      }
      pthread_mutex_unlock(&_lock);
    }

    return _tp;
  }

private:
  ThreadPool(int num = defaultCap)
      : _threadpool(num)
  {
    pthread_mutex_init(&_mutex, nullptr);
    pthread_cond_init(&_cond, nullptr);
  }

  ~ThreadPool()
  {
    pthread_mutex_destroy(&_mutex);
    pthread_cond_destroy(&_cond);
  }

private:
  std::vector<threadData> _threadpool;
  std::queue<T> _taskqueue;

  pthread_mutex_t _mutex;
  pthread_cond_t _cond;

  static ThreadPool<T> *_tp;
  static pthread_mutex_t _lock;
};

template <class T>
ThreadPool<T> *ThreadPool<T>::_tp = nullptr;

template <class T>
pthread_mutex_t ThreadPool<T>::_lock = PTHREAD_MUTEX_INITIALIZER;
